﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LongNet.Util
{
    /// <summary>
    /// The available log levels for LogManager.
    /// </summary>
    public class LogLevel : IComparable
    {
        #region Fields

        private static LogLevel[] levelByOrdinal;

        public static readonly LogLevel MaxLevel;
        public static readonly LogLevel MinLevel;

        /// <summary>
        /// The Debug level.
        /// </summary>
        public static readonly LogLevel Debug;

        /// <summary>
        /// The Info level.
        /// </summary>
        public static readonly LogLevel Info;

        /// <summary>
        /// The Warn level.
        /// </summary>
        public static readonly LogLevel Warn;

        /// <summary>
        /// The Error level.
        /// </summary>
        public static readonly LogLevel Error;

        /// <summary>
        /// The Fatal level.
        /// </summary>
        public static readonly LogLevel Fatal;

        /// <summary>
        /// The Off level.
        /// </summary>
        public static readonly LogLevel Off;

        #endregion

        #region Constructors

        static LogLevel()
        {
            int l = 0;

            Debug = new LogLevel("Debug", l++);
            Info = new LogLevel("Info", l++);
            Warn = new LogLevel("Warn", l++);
            Error = new LogLevel("Error", l++);
            Fatal = new LogLevel("Fatal", l++);
            Off = new LogLevel("Off", l++);

            levelByOrdinal = new LogLevel[] { Debug, Info, Warn, Error, Fatal, Off };
            MinLevel = levelByOrdinal[0];
            MaxLevel = levelByOrdinal[levelByOrdinal.Length - 2];
        }

        // to be changed into public in the future.
        private LogLevel(string name, int ordinal)
        {
            this.Name = name;
            this.UppercaseName = name.ToUpper();
            this.LowercaseName = name.ToLower();
            this.Ordinal = ordinal;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets the name of the log level.
        /// </summary>
        public string Name { get; private set; }

        /// <summary>
        /// Gets the name of the logger in upper case.
        /// </summary>
        public string UppercaseName { get; private set; }

        /// <summary>
        /// Gets the name of the logger in lower case.
        /// </summary>
        public string LowercaseName { get; private set; }

        internal int Ordinal { get; private set; }

        #endregion

        #region Methods

        /// <summary>
        /// Gets the <see cref="LogLevel"/> that corresponds to the specified ordinal.
        /// </summary>
        /// <param name="ordinal">The ordinal.</param>
        /// <returns>The <see cref="LogLevel"/> instance. For 0 it returns <see cref="LogLevel.Debug"/>, 1 gives <see cref="LogLevel.Info"/> and so on</returns>
        public static LogLevel FromOrdinal(int ordinal)
        {
            return levelByOrdinal[ordinal];
        }

        /// <summary>
        /// Returns the <see cref="T:NLog.LogLevel"/> that corresponds to the supplied <see langword="string" />.
        /// </summary>
        /// <param name="s">the texual representation of the log level</param>
        /// <returns>the enumeration value.</returns>
        public static LogLevel FromString(string s)
        {
            // case sensitive search first
            for (int i = 0; i < levelByOrdinal.Length; ++i)
            {
                if (levelByOrdinal[i].Name == s)
                    return levelByOrdinal[i];
            }

            // case insensitive search
            for (int i = 0; i < levelByOrdinal.Length; ++i)
            {
                if (0 == String.Compare(levelByOrdinal[i].Name, s, true))
                    return levelByOrdinal[i];
            }

            throw new ArgumentException("Unknown log level: " + s);
        }

        /// <summary>
        /// Returns a string representation of the log level.
        /// </summary>
        /// <returns>Log level name.</returns>
        public override string ToString()
        {
            return this.Name;
        }

        /// <summary>
        /// Compares the level to the other <see cref="LogLevel"/> object.
        /// </summary>
        /// <param name="obj">the object object</param>
        /// <returns>
        /// a value less than zero when this logger's <see cref="Ordinal"/> is 
        /// less than the other logger's ordinal, 0 when they are equal and 
        /// greater than zero when this ordinal is greater than the
        /// other ordinal.
        /// </returns>
        public int CompareTo(object obj)
        {
            LogLevel l = (LogLevel)obj;
            return this.Ordinal - l.Ordinal;
        }

        #endregion

        #region Operators

        /// <summary>
        /// Compares two <see cref="LogLevel"/> objects 
        /// and returns a value indicating whether 
        /// the first one is less than or equal to the second one.
        /// </summary>
        /// <param name="a">The first level.</param>
        /// <param name="b">The second level.</param>
        /// <returns>The value of <c>a.Ordinal &lt;= b.Ordinal</c></returns>
        public static bool operator <=(LogLevel a, LogLevel b)
        {
            return a.Ordinal <= b.Ordinal;
        }

        /// <summary>
        /// Compares two <see cref="LogLevel"/> objects 
        /// and returns a value indicating whether 
        /// the first one is greater than or equal to the second one.
        /// </summary>
        /// <param name="a">The first level.</param>
        /// <param name="b">The second level.</param>
        /// <returns>The value of <c>a.Ordinal &gt;= b.Ordinal</c></returns>
        public static bool operator >=(LogLevel a, LogLevel b)
        {
            return a.Ordinal >= b.Ordinal;
        }

        /// <summary>
        /// Compares two <see cref="LogLevel"/> objects 
        /// and returns a value indicating whether 
        /// the first one is less than the second one.
        /// </summary>
        /// <param name="a">The first level.</param>
        /// <param name="b">The second level.</param>
        /// <returns>The value of <c>a.Ordinal &lt; b.Ordinal</c></returns>
        public static bool operator <(LogLevel a, LogLevel b)
        {
            return a.Ordinal < b.Ordinal;
        }

        /// <summary>
        /// Compares two <see cref="LogLevel"/> objects 
        /// and returns a value indicating whether 
        /// the first one is greater than the second one.
        /// </summary>
        /// <param name="a">The first level.</param>
        /// <param name="b">The second level.</param>
        /// <returns>The value of <c>a.Ordinal &gt; b.Ordinal</c></returns>
        public static bool operator >(LogLevel a, LogLevel b)
        {
            return a.Ordinal > b.Ordinal;
        }

        #endregion
    } 
}
